package com.mask.im.business.handler;

import com.alibaba.fastjson.JSON;
import com.mask.im.business.annotation.AesCrypto;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

/**
 * 响应体加密处理器
 * 负责对带有@AesCrypto注解的响应体进行加密处理
 * 
 * @author jian
 */
@Log4j2
@ControllerAdvice
@Component
public class EncryptResponseBodyAdviceHandler implements ResponseBodyAdvice<Object> {

    @Value("${crypto.charset:UTF-8}")
    private String charset;
    
    @Value("${crypto.key:}")
    private String key;
    
    @Autowired
    private CryptoService cryptoService;

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> aClass) {
        // 检查方法或类是否有@AesCrypto注解
        return hasAesCryptoAnnotation(returnType);
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                                Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                ServerHttpRequest request, ServerHttpResponse response) {
        try {
            // 检查是否需要加密
            if (!shouldEncrypt(returnType)) {
                return body;
            }
            
            log.debug("开始加密响应体: {}", returnType.getMethod().getName());
            
            // 验证配置
            if (key == null || key.trim().isEmpty()) {
                log.error("加密密钥未配置");
                return body;
            }
            
            // 如果body为null，直接返回
            if (body == null) {
                return body;
            }
            
            // 将响应体转换为JSON字符串
            String jsonBody = convertToJson(body);
            
            // 加密响应体
            String encryptedBody = cryptoService.encrypt(jsonBody, key, charset);
            
            log.debug("响应体加密完成，原始大小: {} bytes, 加密后大小: {} bytes", 
                    jsonBody.length(), encryptedBody.length());
            
            // 设置响应头
            response.getHeaders().set("Content-Type", "application/json;charset=" + charset);
            response.getHeaders().set("X-Encrypted", "true");
            
            return encryptedBody;
            
        } catch (Exception e) {
            log.error("响应体加密失败", e);
            // 加密失败时返回原始响应体
            return body;
        }
    }

    /**
     * 检查是否有AES加密注解
     */
    private boolean hasAesCryptoAnnotation(MethodParameter returnType) {
        if (returnType.getMethod() == null) {
            return false;
        }
        
        // 检查方法级别的注解
        if (returnType.getMethod().isAnnotationPresent(AesCrypto.class)) {
            return true;
        }
        
        // 检查类级别的注解
        return returnType.getMethod().getDeclaringClass().isAnnotationPresent(AesCrypto.class);
    }

    /**
     * 检查是否需要加密
     */
    private boolean shouldEncrypt(MethodParameter returnType) {
        AesCrypto methodAnnotation = returnType.getMethod().getAnnotation(AesCrypto.class);
        AesCrypto classAnnotation = returnType.getMethod().getDeclaringClass().getAnnotation(AesCrypto.class);
        // 优先使用方法级别的注解
        AesCrypto annotation = methodAnnotation != null ? methodAnnotation : classAnnotation;
        return annotation != null && annotation.open();
    }

    /**
     * 将对象转换为JSON字符串
     */
    private String convertToJson(Object body) {
        if (body instanceof String) {
            return (String) body;
        }
        
        try {
            return JSON.toJSONString(body);
        } catch (Exception e) {
            log.warn("对象转JSON失败，使用toString()方法", e);
            return body.toString();
        }
    }
}
